#include <iostream>
#include <string>
#include <fstream>

#include "vini/vini.h"


std::string std::to_string(bool value){
	if (value){
		return "true";
	}
	else{
		return "false";
	}
}

std::string std::to_string(std::string value){
	return std::string("\"" + value + "\"");
}

std::string std::to_string(const char *value){
	return std::string("\"" + std::string(value) + "\"");
}

void RemoveComments(std::string &line);

void RemoveSpacesAndTabs(std::string &str);

void RemoveComments(std::string &line, const std::string mark){
	size_t pos = line.find(mark);
	if (std::string::npos != pos){
		line.erase(pos);
	}
}

void RemoveComments(std::string &line)
{
	RemoveComments(line, ";");
	RemoveComments(line, "#");
	RemoveComments(line, "//");
}

void RemoveSpacesAndTabs(std::string &str){
	if (str.empty()){
		return;
	}
	while (*(str.begin()) == ' ' || *(str.begin()) == '\t'){
		str.erase(str.begin());
	}
	while (*(--str.end()) == ' ' || *(--str.end()) == '\t'){
		str.erase(--str.end());
	}
}

Vini::Vini(std::string filename) :mFile(filename), mLines(), isWritten(false){
	std::ifstream file(mFile, std::ios::in);
	if (!file.is_open()){
		std::cerr << "open file \"" << mFile << "\" failed." << std::endl;
		return;
	}
	std::string line;
	while (getline(file, line)){
		mLines.push_back(line);
	}
	file.close();
}

Vini::~Vini(){
	if (isWritten){
		std::ofstream file(mFile, std::ios::trunc);
		if (!file.is_open()){
			std::cerr << "open file \"" << mFile << "\" failed." << std::endl;
			return;
		}
		for (auto line : mLines){
			file << line << std::endl;
		}
		file.close();
	}
}

bool getSection(std::string line, std::string section){
	if (line.empty())
		return false;
	if ((*line.begin() == '[') && (*(--line.end()) == ']')){
		line.erase(line.begin());
		line.erase(--line.end());
		RemoveSpacesAndTabs(line);
		if (line == section)
			return true;
	}
	return false;
}

bool getValue(std::string line, std::string key, std::string &value){
	size_t pos = line.find('=', 0);
	if (pos != std::string::npos){
		std::string tmp = line.substr(0, pos);
		RemoveSpacesAndTabs(tmp);
		if (tmp == key){
			value = line.substr(pos, std::string::npos);
			value.erase(value.begin());
			RemoveSpacesAndTabs(value);
			if (value.empty()){
				return false;
			}
			return true;
		}
	}
	return false;
}

bool setValue(std::string &line, const std::string key, const std::string value){
	size_t pos = line.find('=', 0);
	if (pos != std::string::npos){
		std::string tmp = line.substr(0, pos);
		RemoveSpacesAndTabs(tmp);
		if (tmp == key){
			tmp = line.substr(pos, std::string::npos);
			tmp.erase(tmp.begin());
			RemoveComments(tmp);
			RemoveSpacesAndTabs(tmp);
			if (tmp.empty()){
				pos = line.find('=');
				line.insert(++pos, value);
			}
			else{
				pos = line.find(tmp);
				line.replace(pos, tmp.size(), value);
			}
			return true;
		}
	}
	return false;
}

bool Vini::readString(const std::string section, const std::string key, std::string &value) const{
	if (section.empty() || key.empty()){
		std::cerr << "section  or key is empty" << std::endl;
		return false;
	}
	bool isFound = false;
	for (auto line : mLines){
		RemoveComments(line);
		RemoveSpacesAndTabs(line);
		if (!isFound){
			if (getSection(line, section)){
				isFound = true;
			} 
			continue;
		}
		if (*line.begin() == '[' && *(--line.end()) == ']')
			break;
		if (getValue(line, key, value))
			return true;
	}
	return false;
}

bool Vini::writeString(const std::string section, const std::string key, const std::string value){
	if (section.empty() || key.empty()){
		std::cerr << "section  or key is empty" << std::endl;
		return false;
	}
	bool isFound = false;
	std::size_t size = mLines.size();
	std::string line;
	for (std::size_t i = 0; i < size; ++i){
		line = mLines[i];
		RemoveComments(line);
		RemoveSpacesAndTabs(line);
		if (!isFound){
			if (getSection(line, section)){
				isFound = true;
			}
			continue;
		}
		if (*line.begin() == '[' && *(--line.end()) == ']'){
			mLines.insert(mLines.begin() + i,std::string(key + "=" + value));
			isWritten = true;
			return true;
		}
		if (setValue(mLines[i], key, value)){
			isWritten = true;
			return true;
		}
	}
	std::string tmp;
	std::string endLine = *(--mLines.end());
	tmp += std::string("[" + section + "]");
	mLines.push_back(tmp);
	mLines.push_back(std::string(key + "=" + value));
	isWritten = true;
	return true;
}
